/*
** StockDB
**
** Copyright (C) 2012 - ? doublegsoft
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>

#ifdef WIN32

#include <winsock2.h>
#include <ws2tcpip.h>

#else

#include <netdb.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#endif // #ifdef WIN32

#include "stockdb.h"

#include "request.h"
#include "process.h"
#include "connect.h"
#include "protocol.h"

/*!
**
*/
int
main(void)
{

  fd_set masterfds;                   // master file descriptor list
  fd_set readfds;                     // temp file descriptor list for select()
  int fdmax;                          // maximum file descriptor number
  int err;

  int listener, newfd, yes;

  struct sockaddr_in server, remote;

#ifdef WIN32
  int addrlen;
  WSADATA wsadata;
  if ((err = WSAStartup(0x0202, &wsadata)) != 0)
  {
    fprintf(stderr, "WSAStartup() failed with error %d\n", err);
    WSACleanup();
    exit(1);
  }
#else
  unsigned int addrlen;
#endif

  listener = socket(AF_INET, SOCK_STREAM, 0);
  if (listener < 0)
  {
    fprintf(stderr, "socket error: %d - %s\n", listener, gai_strerror(listener));
    exit(1);
  }
#ifdef WIN32
  setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(int));
#else
  setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
#endif

  memset(&server, 0, sizeof server);
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = htonl(INADDR_ANY);
  server.sin_port = htons(PORT);

  if ((err = bind(listener, (struct sockaddr*) &server, sizeof(server))) < 0)
  {
    fprintf(stderr, "bind error: %d - %s\n", err, gai_strerror(err));
#ifdef WIN32
    closesocket(listener);
    WSACleanup();
#else
    close(listener);
#endif
    exit(1);
  }

#ifdef WIN32

  unsigned long nonblock = 1;
  if (ioctlsocket(listener, FIONBIO, &nonblock) == SOCKET_ERROR)
  {
    fprintf(stderr, "ioctlsocket() failed with error %d\n", WSAGetLastError());
    return 1;
  }
#endif

  char buf[256];                      // buffer for client data
  int recved;

  int i;

  // clear the master and temp sets
  FD_ZERO(&masterfds);
  FD_ZERO(&readfds);

  // listen
  if (listen(listener, BACKLOG) == -1)
  {
    perror("listen");
    exit(3);
  }

  // add the listener to the master set
  FD_SET(listener, &masterfds);

  // keep track of the biggest file descriptor
  fdmax = listener; // so far, it's this one

  // main loop
  for(;;)
  {
    readfds = masterfds; // copy it
    if (select(fdmax + 1, &readfds, NULL, NULL, NULL) == -1)
    {
      perror("select");
      exit(4);
    }
    // run through the existing connections looking for data to read
    for(i = 0; i <= fdmax; i++)
    {
      if (FD_ISSET(i, &readfds)) // we got one!!
      {
        if (i == listener) {
          // handle new connections
          addrlen = sizeof(struct sockaddr_in);
          newfd = accept(listener, (struct sockaddr *)&remote, &addrlen);
          if (newfd == -1)
          {
            perror("accept");
          }
          else
          {
            FD_SET(newfd, &masterfds); // add to master set
            if (newfd > fdmax) // keep track of the max
            {
              fdmax = newfd;
            }
//#ifndef WIN32
            printf("stockdb: new connection from %s on " "socket %d\n",
                inet_ntoa(remote.sin_addr), newfd);
//#endif
          }
        }
        else
        {
          // handle data from a client
          memset(buf, 0, sizeof(buf));
          if ((recved = recv(i, buf, sizeof buf, 0)) <= 0)
          {
            // got error or connection closed by client
            if (recved < 0)
            {
              // connection closed
              fprintf(stderr, "recv() error: %s\n", gai_strerror(recved));
            }
            printf("stockdb: socket %d is closing...\n", i);
            stockdb_close_connection(i, &masterfds);
            stockdb_delete_reqbuf(i);
          }
          else
          {
            reqbuf_t *found = stockdb_find_reqbuf(i);
            stockdb_append_data(found, buf, recved);
            int err = 0;
            // invalid protocol
            if ((err = stockdb_protocol_header(found->data, found->length)) < 0)
            {
              fprintf(stderr, "invalid stockdb protocol data: %s\n", found->data);
              stockdb_close_connection(i, &masterfds);
              stockdb_delete_reqbuf(i);
              found = NULL;
            }
            if (err >= 0)
            {
              int end = stockdb_protocol_footer(found->data, found->length);
              if (end > 0)
              {
                printf("received: %s\n", stockdb_protocol_data(found->data, end));
                // TODO: make a working thread to process data
                stockdb_delete_reqbuf(i);
                found = NULL;
              }
            }
            if (found)
              printf("received raw data: %s\n", found->data);
//            // we got some data from a client
//            for (j = 0; j <= fdmax; j++)
//            {
//              // send to everyone!
//              if (FD_ISSET(j, &masterfds))
//              {
//                // except the listener and ourselves
//                if (j != listener && j != i)
//                {
//                  if (send(j, buf, recved, 0) == -1)
//                  {
//                    perror("send");
//                  }
//                }
//              }
//            } // for (j = 0; j <= fdmax; j++)
          } // if recv
        } // END handle data from client
      } // END got new incoming connection
    } // END looping through file descriptors
  } // for(;;) -- and you thought it would never end!
  return 0;
}


